<?php

namespace plugin\admin\app\service\v1;

use plugin\admin\app\common\fast\Random;
use plugin\admin\app\common\Util;
use plugin\admin\app\constant\v1\CommonConstant;
use plugin\admin\app\model\V1Order;
use plugin\admin\app\model\V1OrderNode;
use plugin\admin\app\model\V1OrderNodeExchangeLog;
use plugin\admin\app\model\V1OrderNodeExchangeLogTp;
use plugin\admin\app\model\V1OrderNodeTp;
use support\exception\BusinessException;
use Webman\RedisQueue\Redis;

/**
 * 订单服务类
 **/
class OrderService
{

    /**
     * 购买
     *
     * @param array $params
     * @param array $admin
     **/
    public static function create($params, $admin)
    {
        list($lineId, $gameId, $ipSegment, $duration, $custom_type, $account, $pass, $group, $remark, $port, $data) = array_values($params);

        // 节点数据
        $totalRequested = 0;
        $regionIds = [];
        $regionRequests = [];
        foreach ($data as $value) {
            $totalRequested += (int)$value['number'];
            $regionIds[] = $value['region_id'];
            $regionRequests[$value['region_id']] = (int)$value['number'];
        }

        $db = Util::db();

        $testDuration = [
            CommonConstant::TIME_0,
            CommonConstant::TIME_1,
            CommonConstant::TIME_2
        ];
        $isTestDuration = in_array($duration, $testDuration);

        // 线路
        $line = $db->table('wa_v1_line')
            ->where('id', $lineId)
            ->where('deleted', CommonConstant::DELETE_0)
            ->value('id');
        if (!$line) {
            throw new BusinessException('线路不存在！');
        }

        // 游戏
        $game = $db->table('wa_v1_game')
            ->where('id', $gameId)
            ->where('deleted', CommonConstant::DELETE_0)
            ->value('id');
        if (!$game) {
            throw new BusinessException('游戏不存在！');
        }

        // 用户权限和价格
        $userExtendInfo = self::getUserExtendInfo($db, $lineId, $duration, $admin, $totalRequested, $isTestDuration);

        // 地区列表
        $regions = $db->table('wa_v1_region')
            ->where('deleted', CommonConstant::DELETE_0)
            ->where('line_id', $lineId)
            ->whereIn('id', $regionIds)
            ->select(['id', 'name'])
            ->get();
        if ($regions->isEmpty()) {
            throw new BusinessException('地区不存在！');
        }
        if (count($regions) != count($regionIds)) {
            throw new BusinessException('部分地区不存在！');
        }

        // 地区Ids
        $regionIds = $regions->pluck('id')->toArray();

        // 节点列表
        $nodes = $db->table('wa_v1_node_static')
            ->where('deleted', CommonConstant::DELETE_0)
            ->where('line_id', $lineId)
            ->whereIn('region_id', $regionIds)
            ->get();
        if ($nodes->isEmpty()) {
            throw new BusinessException('没有可用的节点！');
        }

        // 每个地区分组节点
        $regionNodes = $nodes->groupBy('region_id');

        // 服务器Ids
        $serverIds = $nodes->pluck('server_id')->unique()->toArray();

        // 服务器列表
        $servers = $db->table('wa_v1_server')
            ->where('deleted', CommonConstant::DELETE_0)
            ->whereIn('id', $serverIds)
            ->get()
            ->keyBy('id');

        // 每个服务器的已售数量（所有游戏和地区的订单）
        $serverUsedCounts = self::getServerUsedCounts($db, $lineId, $serverIds, $testDuration);

        // 当前游戏+地区的已售节点
        $nodeUsed = self::getNodeUsed($db, $lineId, $gameId, $regionIds, $testDuration);

        // 过滤掉已被当前游戏使用的节点
        $availableNodes = $nodes->filter(function ($node) use ($nodeUsed) {
            return !in_array($node->code, $nodeUsed);
        });

        // 重新按地区分组可用节点
        $availableRegionNodes = $availableNodes->groupBy('region_id');

        // 计算每个服务器的空闲数量
        $serverAvailableCounts = [];
        foreach ($servers as $serverId => $server) {
            $usedCount = $serverUsedCounts[$serverId] ?? 0;
            $serverAvailableCounts[$serverId] = max(0, $server->max_limit - $usedCount);
        }

        $allocatedNodes = [];

        // 处理每个地区的节点分配
        foreach ($regionRequests as $regionId => $number) {
            if (!isset($availableRegionNodes[$regionId])) {
                throw new BusinessException("地区 {$regionId} 在当前游戏下没有可用节点");
            }

            $regionNodeList = $availableRegionNodes[$regionId];

            // 检查当前游戏+地区的可购买数量
            if (count($regionNodeList) < $number) {
                throw new BusinessException("地区 {$regionId} 在当前游戏下可用节点不足，需要 {$number} 个，但只有 " . count($regionNodeList) . " 个可用");
            }

            // 按服务器空闲数量排序节点
            $sortedNodes = self::sortNodesByServerAvailability($regionNodeList, $serverAvailableCounts);

            // 分配节点
            $allocatedCount = 0;
            foreach ($sortedNodes as $node) {
                if ($allocatedCount >= $number) {
                    break;
                }

                // 检查服务器是否还有空闲
                if (($serverAvailableCounts[$node->server_id] ?? 0) <= 0) {
                    continue; // 服务器已满，跳过
                }

                $allocatedNodes[] = $node;
                $allocatedCount++;

                // 更新服务器空闲数量
                $serverAvailableCounts[$node->server_id]--;
            }

            if ($allocatedCount < $number) {
                throw new BusinessException("地区 {$regionId} 由于服务器限制，只能分配 {$allocatedCount} 个节点，需要 {$number} 个");
            }
        }

        $allocatedNumber = count($allocatedNodes);

        // 价格计算
        if ($isTestDuration) {
            $amount = 0;
            $totalAmount = 0;
        } else {
            $amount = $userExtendInfo->{$duration};
            $totalAmount = $allocatedNumber * $amount;
            if ($admin['money'] < $totalAmount) {
                throw new BusinessException('余额不足！');
            }
        }

        $orderNo = get_order_sn($admin['id']);
        $time = time();
        $date = date('Y-m-d H:i:s');
        $sec = CommonConstant::get_duration_sec_data()[$duration][1];
        $expire_time = $time + $sec;

        $orderData = [];
        $orderData['user_id'] = $admin['id'];
        $orderData['user_name'] = $admin['username'];
        $orderData['order_no'] = $orderNo;
        $orderData['line_id'] = $lineId;
        $orderData['game_id'] = $gameId;
        $orderData['duration'] = $duration;
        $orderData['amount'] = $totalAmount;
        $orderData['expire_time'] = date('Y-m-d H:i:s', $expire_time);
        $orderData['remark'] = $remark;
        $orderData['source'] = CommonConstant::DEFAULT_0;

        $insertOrderNodeData = [];
        $insertExchangeLogData = [];
        $apiDataAdd = [];
        $accountBase = ($custom_type == CommonConstant::DEFAULT_3) ? Random::randCode(8, 5) : null;
        foreach ($allocatedNodes as $key => $item) {
            switch ($custom_type) {
                case CommonConstant::DEFAULT_2:
                    $nodeAccount = Random::build('alnum', 8);
                    $nodePass = Random::build('numeric', 8);
                    break;
                case CommonConstant::DEFAULT_3:
                    $nodeAccount = $accountBase . $key;
                    $nodePass = Random::build('numeric', 8);
                    break;
                default:
                    $nodeAccount = $account;
                    $nodePass = $pass;
            }
            $orderNodeData = [];
            $orderNodeData['created_at'] = $date;
            $orderNodeData['user_id'] = $orderData['user_id'];
            $orderNodeData['user_name'] = $orderData['user_name'];
            $orderNodeData['order_no'] = $orderNo;
            $orderNodeData['line_id'] = $lineId;
            $orderNodeData['game_id'] = $gameId;
            $orderNodeData['duration'] = $duration;
            $orderNodeData['amount'] = $amount;
            $orderNodeData['expire_time'] = $orderData['expire_time'];
            $orderNodeData['group'] = $group;
            $orderNodeData['remark'] = $remark;
            $orderNodeData['source'] = CommonConstant::DEFAULT_0;
            $orderNodeData['region_id'] = $item->region_id;
            $orderNodeData['server_id'] = $item->server_id;
            $orderNodeData['code'] = $item->code;
            $orderNodeData['ip'] = $item->ip;
            $orderNodeData['port'] = $item->port;
            $orderNodeData['ip_ems'] = $item->ip_ems;
            $orderNodeData['account'] = $nodeAccount;
            $orderNodeData['pass'] = $nodePass;
            $serverInfo = $servers->get($item->server_id);
            $orderNodeData['domain'] = $serverInfo->domain;
            $url = 'http://' . $serverInfo->ip . ':' . $serverInfo->port . '/user/';
            $orderNodeData['data'] = $url . '|' . $serverInfo->key;
            $insertOrderNodeData[] = $orderNodeData;

            $exchangeLogData = [];
            $exchangeLogData['user_id'] = $orderData['user_id'];
            $exchangeLogData['user_name'] = $orderData['user_name'];
            $exchangeLogData['line_id'] = $lineId;
            $exchangeLogData['type'] = CommonConstant::DEFAULT_0;
            $exchangeLogData['original_info'] = '';
            $exchangeLogData['new_info'] = $item->ip . '|' . $item->port . '|' . $nodeAccount . '|' . $nodePass . '|' . $orderData['expire_time'];
            $insertExchangeLogData[] = $exchangeLogData;

            $apiData = [];
            $apiData['url'] = $url;
            $apiData['ip'] = $serverInfo->ip;
            $apiData['port'] = $serverInfo->port;
            $apiData['key'] = $serverInfo->key;
            $apiData['username'] = $nodeAccount;
            $apiData['password'] = $nodePass;
            $apiData['user_type'] = $serverInfo->line_type; // 线路类型，1.socks5 2.HTTP -1.通用
            $apiData['limit_up'] = $serverInfo->bandwidth_up;
            $apiData['limit_down'] = $serverInfo->bandwidth_down;
            $apiData['maxcon'] = $serverInfo->link_url_number;
            $apiData['enable'] = true; // 是否启用此用户，用于临时禁用
            $apiData['shadowtime'] = $orderData['expire_time']; // 到期时间 注意要补0
            $apiData['info'] = 1; // 预留参数
            $apiData['bindip'] = $item->ip_ems; // 绑定的内网IP参数，如果为空则不限制
            $apiData['uid'] = $orderData['user_name'];
            $apiDataAdd[] = $apiData;
        }
        $logData = [
            'admin_id' => $admin['id'],
            'admin_name' => $admin['username'],
            'user_id' => $admin['id'],
            'user_name' => $admin['username'],
            'type' => CommonConstant::MONEY_LOG_TYPE_4,
            'change_type' => CommonConstant::DEFAULT_20,
            'money' => $totalAmount,
            'memo' => '购买 ' . $allocatedNumber . '个节点【' . CommonConstant::get_duration_type_list()[$duration] . '】',
            'line_id' => $lineId,
            'duration' => $duration,
        ];

        $moneyLogId = $db->transaction(function () use ($db, $orderData, $insertOrderNodeData, $insertExchangeLogData, $logData, $isTestDuration, $duration) {
            // 添加订单
            $orderId = doInsert($orderData, new V1Order());

            // 添加订单节点
            $insertOrderNodeData = array_map(function ($item) use ($orderId) {
                $item['order_id'] = $orderId;
                $item['original_id'] = $orderId;
                return $item;
            }, $insertOrderNodeData);
            V1OrderNode::insert($insertOrderNodeData);

            // 添加订单节点变动
            $insertExchangeLogData = array_map(function ($item) use ($orderId) {
                $item['order_id'] = $orderId;
                return $item;
            }, $insertExchangeLogData);
            $db->table('wa_v1_order_node_exchange_log')->insert($insertExchangeLogData);

            $orderNodeIds = $db->table('wa_v1_order_node')
                ->where('order_id', $orderId)
                ->pluck('id')
                ->toArray();
            // 余额变动
            $logData['order_id'] = $orderId;
            $logData['order_node_ids'] = implode(',', $orderNodeIds);
            $logId = UserService::money($logData, $logData['user_id'], []);
            return $logId;
        });

        if (!$isTestDuration) {
            // 队列名
            // 数据，可以直接传数组，无需序列化
            // 投递消息
            Redis::send('commission', ['commission_type' => CommonConstant::DEFAULT_1, 'type' => CommonConstant::MONEY_LOG_TYPE_4, 'money_log_id' => $moneyLogId]);
        }

        $apiResAdd = Util::batchUserApi('add', $apiDataAdd);
        return V(0, $apiResAdd ? implode(',', $apiResAdd) : '购买成功', $insertOrderNodeData);
    }

    /**
     * 用户权限和价格
     **/
    public static function getUserExtendInfo($db, $lineId, $duration, $admin, $totalRequested, $isTestDuration)
    {
        if ($isTestDuration) {
            $field = ['id', 'test_number'];
        } else {
            $field = ['id', 'test_number', $duration];
        }
        $userExtendInfo = $db->table('wa_v1_user_extend')
            ->where('user_id', $admin['id'])
            ->where('line_id', $lineId)
            ->select($field)
            ->first();
        if (!$userExtendInfo) {
            throw new BusinessException('系统价格配置不存在！');
        }
        if ($isTestDuration) {
            if ($totalRequested > $userExtendInfo->test_number) {
                throw new BusinessException('测试卡数量不能超过当前测试卡数量！');
            }
            $activeTestNodes = self::orderNumber($admin, CommonConstant::DEFAULT_2, $lineId);
            if ($activeTestNodes > 0 && $totalRequested > ($userExtendInfo->test_number - $activeTestNodes)) {
                throw new BusinessException('当前测试卡数量不足！');
            }
        }
        return $userExtendInfo;
    }

    /**
     * 每个服务器的已售数量（所有游戏和地区的订单）
     **/
    public static function getServerUsedCounts($db, $lineId, $serverIds, $testDuration)
    {
        $serverUsedCounts = [];
        if (!empty($serverIds)) {
            $serverUsedCounts = $db->table('wa_v1_order_node')
                ->where('deleted', CommonConstant::DELETE_0)
                ->whereIn('status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1])
                ->whereIn('refund_status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1, CommonConstant::DEFAULT_3])
                ->where('line_id', $lineId)
                ->whereIn('server_id', $serverIds)
                ->whereNotIn('duration', $testDuration)
                ->select('server_id', $db->raw('COUNT(id) as count'))
                ->groupBy('server_id')
                ->pluck('count', 'server_id')
                ->toArray();
        }
        return $serverUsedCounts;
    }

    /**
     * 当前游戏+地区的已售数量
     **/
    public static function getRegionGameUsedCounts($db, $lineId, $gameId, $regionIds, $testDuration)
    {
        $regionGameUsedCounts = $db->table('wa_v1_order_node')
            ->where('deleted', CommonConstant::DELETE_0)
            ->whereIn('status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1])
            ->whereIn('refund_status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1, CommonConstant::DEFAULT_3])
            ->where('line_id', $lineId)
            ->where('game_id', $gameId)
            ->whereIn('region_id', $regionIds)
            ->whereNotIn('duration', $testDuration)
            ->select('region_id', $db->raw('COUNT(id) as count'))
            ->groupBy('region_id')
            ->get()
            ->pluck('count', 'region_id')
            ->toArray();
        return $regionGameUsedCounts;
    }

    /**
     * 当前游戏+地区的已售节点
     **/
    public static function getNodeUsed($db, $lineId, $gameId, $regionIds, $testDuration)
    {
        $nodeUsed = $db->table('wa_v1_order_node')
            ->where('deleted', CommonConstant::DELETE_0)
            ->whereIn('status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1])
            ->whereIn('refund_status', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_1, CommonConstant::DEFAULT_3])
            ->where('line_id', $lineId)
            ->where('game_id', $gameId)
            ->whereIn('region_id', $regionIds)
            ->whereNotIn('duration', $testDuration)
            ->pluck('code')
            ->toArray();
        return $nodeUsed;
    }

    /**
     * 按服务器空闲数量排序节点
     **/
    public static function sortNodesByServerAvailability($nodes, $serverAvailableCounts)
    {
        $nodesArray = $nodes->toArray();

        // 按服务器空闲数量降序排序
        usort($nodesArray, function ($a, $b) use ($serverAvailableCounts) {
            $aAvailable = $serverAvailableCounts[$a->server_id] ?? 0;
            $bAvailable = $serverAvailableCounts[$b->server_id] ?? 0;

            if ($aAvailable == $bAvailable) {
                return 0;
            }

            return $aAvailable > $bAvailable ? -1 : 1;
        });

        return $nodesArray;
    }

    /**
     * 按服务器空闲数量排序节点，相同空闲数量的节点随机打乱（优化版）
     */
    public static function sortNodesByServerAvailabilityRandom($nodes, $serverAvailableCounts)
    {
        $nodesArray = $nodes->toArray();

        // 使用usort一次性完成排序和随机化
        usort($nodesArray, function ($a, $b) use ($serverAvailableCounts) {
            $availableA = $serverAvailableCounts[$a->server_id] ?? 0;
            $availableB = $serverAvailableCounts[$b->server_id] ?? 0;

            // 首先按服务器空闲数量降序排序
            if ($availableA != $availableB) {
                return $availableB - $availableA;
            }

            // 相同空闲数量时随机排序
            return mt_rand(-1, 1);
        });

        return $nodesArray;
    }


    /**
     * 节点数量
     *
     * @param array $admin
     * @param integer $status 状态:0=未过期的节点,1=到期过期的节点,2=未过期的测试卡节点，4=今天的已调换数量,5=今天的退款数量
     * @param integer $lineId
     **/
    public static function orderNumber($admin, $status, $lineId = 0)
    {
        $where = [];
        switch ($status) {
            case CommonConstant::DEFAULT_0:
                $where[] = ['status', '=', CommonConstant::DEFAULT_0];
                $where[] = ['refund_status', 'in', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_3]];
                break;
            case CommonConstant::DEFAULT_1:
                $where[] = ['status', 'IN', [CommonConstant::DEFAULT_1, CommonConstant::DEFAULT_2]];
                $where[] = ['refund_status', 'in', [CommonConstant::DEFAULT_0, CommonConstant::DEFAULT_3]];
                break;
            case CommonConstant::DEFAULT_2:
                $where[] = ['status', '=', CommonConstant::DEFAULT_0];
                $where[] = ['duration', 'IN', [CommonConstant::TIME_0, CommonConstant::TIME_1, CommonConstant::TIME_2]];
                break;
            case CommonConstant::DEFAULT_3:
                $where[] = ['exchange_number', '>', 0];
                break;
            case CommonConstant::DEFAULT_4:
                $query = V1OrderNodeExchangeLogTp::where('user_id', $admin['id'])
                    ->where('type', CommonConstant::DEFAULT_1)
                    ->whereBetween('created_at', [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')]);
                if ($lineId > 0) {
                    $list = $query->where('line_id', $lineId)
                        ->count('id');
                } else {
                    $list = $query
                        ->field('line_id,count(id) as number')
                        ->group('line_id')
                        ->select()
                        ->toArray();
                }
                return $list;
                break;
            case CommonConstant::DEFAULT_5:
                $db = Util::db();
                $query = $db->table('wa_v1_order_node_refund_log')
                    ->where('user_id', $admin['id'])
                    ->whereIn('refund_status', [CommonConstant::DEFAULT_1, CommonConstant::DEFAULT_2])
                    ->whereBetween('created_at', [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')]);
                if ($lineId > 0) {
                    $list = $query->where('line_id', $lineId)
                        ->count('id');
                } else {
                    $list = $query
                        ->select('line_id', $db->raw('count(id) as number'))
                        ->groupBy('line_id')
                        ->get()
                        ->toArray();
                }
                return $list;
                break;
        }
        $query = V1OrderNodeTp::where('deleted', CommonConstant::DELETE_0)
            ->where('user_id', $admin['id'])
            ->where($where);
        if ($lineId > 0) {
            $query->where('line_id', $lineId);
            if ($status == 3) {
                $list = $query->sum('exchange_number');
            } else {
                $list = $query->count('id');
            }
        } else {
            if ($status == 3) {
                $query->field('line_id,sum(exchange_number) as number');
            } else {
                $query->field('line_id,count(id) as number');
            }
            $list = $query->group('line_id')
                ->select()
                ->toArray();
        }
        return $list;
    }

    /**
     * 剩余节点退款数量
     *
     * @param array $admin
     * @param integer $lineId
     **/
    public static function surplusRefundNumber($admin, $lineId)
    {
        $order_node_refund_switch = 0;
        $order_node_refund_time = 0;
        $order_node_refund_number = 0;
        $surplus_refund_number = 0;
        $info = Util::db()->table('wa_v1_user_extend')
            ->where('user_id', $admin['id'])
            ->where('line_id', $lineId)
            ->select('order_node_refund_switch', 'order_node_refund_time', 'order_node_refund_number')
            ->first();
        if ($info) {
            $order_node_refund_switch = $info->order_node_refund_switch;
            $order_node_refund_time = $info->order_node_refund_time;
            $order_node_refund_number = $info->order_node_refund_number;
            if ($order_node_refund_switch == CommonConstant::DEFAULT_1) {
                if ($order_node_refund_number > 0) {
                    $use_refund_number = self::orderNumber($admin, CommonConstant::DEFAULT_5, $lineId);
                    $surplus_refund_number = $order_node_refund_number - $use_refund_number;
                    $surplus_refund_number = $surplus_refund_number > 0 ? $surplus_refund_number : 0;
                }
            }
        }

        return compact('order_node_refund_switch', 'order_node_refund_time', 'order_node_refund_number', 'surplus_refund_number');
    }

}
